﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common;
using FourierTransform;
using System.Windows.Forms;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;

namespace FourierTransformPlugin
{
    public class FourierTransformPlugin : IProcessorPlugin
    {
        #region IProcessorPlugin Members
        ImageProcessApp app;

        public void Load(ImageProcessApp App)
        {
            app = App;    
        }

        public void Unload()
        {
            
        }

        public void ProcessCommand(string commandName, Document doc)
        {
            switch (commandName)
            {
                case "ShowSpectrum":
                    {
                        ShowSpectrum(doc);
                    }
                    break;
                case "ButterworthLowPass":
                    {
                        ButterworthLowPass(doc);
                    }
                    break;
                case "ButterworthHighPass":
                    {
                        ButterworthHighPass(doc);
                    }
                    break;
                case "GuassianLowPass":
                    {
                        GuassianLowPass(doc);
                    }
                    break;
                case "GuassianHighPass":
                    {
                        GuassianHighPass(doc);
                    }
                    break;
            } 
        }

        private void ButterworthLowPass(Document doc)
        {
            int D0 = 100;
            int N = 1;
            ButterworthForm form = new ButterworthForm();
            form.SetOrderEnabled(true);
            form.Text = "Butterworth Low Pass Filter";
            if (form.ShowDialog() == DialogResult.OK)
            {
                D0 = form.GetRadius();
                N = form.GetOrder();
                FrequencyTransformation(doc, (FourierImage fimg) =>
                {
                    int wd2 = fimg.Width/2;
                    int hd2 = fimg.Height/2;
                    for (int j = 0; j < fimg.Height; j++)
                    {
                        for (int i = 0; i < fimg.Width; i++)
                        {
                            float u = i > wd2 ? i - fimg.Width : i;
                            float v = j > hd2 ? j - fimg.Height : j;
                            float factor = 1.0f / (1.0f + (float)Math.Pow(Math.Sqrt(u * u + v * v) / D0, 2 * N));
                            fimg.ValuesB[j * fimg.Width + i] *= factor;
                            fimg.ValuesG[j * fimg.Width + i] *= factor;
                            fimg.ValuesR[j * fimg.Width + i] *= factor;
                        }
                    }
                });
            }
        }

        private void GuassianLowPass(Document doc)
        {
            int D0 = 100;
            ButterworthForm form = new ButterworthForm();
            form.SetOrderEnabled(false);
            form.Text = "Guassian Low Pass Filter";
            if (form.ShowDialog() == DialogResult.OK)
            {
                D0 = form.GetRadius();
                FrequencyTransformation(doc, (FourierImage fimg) =>
                {
                    int wd2 = fimg.Width / 2;
                    int hd2 = fimg.Height / 2;
                    for (int j = 0; j < fimg.Height; j++)
                    {
                        for (int i = 0; i < fimg.Width; i++)
                        {
                            float u = i > wd2 ? i - fimg.Width : i;
                            float v = j > hd2 ? j - fimg.Height : j;
                            float factor = (float)Math.Exp(-(u * u + v * v) / (D0 * D0 * 2));
                            fimg.ValuesB[j * fimg.Width + i] *= factor;
                            fimg.ValuesG[j * fimg.Width + i] *= factor;
                            fimg.ValuesR[j * fimg.Width + i] *= factor;
                        }
                    }
                });
            }
        }

        private void GuassianHighPass(Document doc)
        {
            int D0 = 100;
            ButterworthForm form = new ButterworthForm();
            form.SetOrderEnabled(false);
            form.Text = "Guassian High Pass Filter";
            if (form.ShowDialog() == DialogResult.OK)
            {
                D0 = form.GetRadius();
                FrequencyTransformation(doc, (FourierImage fimg) =>
                {
                    int wd2 = fimg.Width / 2;
                    int hd2 = fimg.Height / 2;
                    for (int j = 0; j < fimg.Height; j++)
                    {
                        for (int i = 0; i < fimg.Width; i++)
                        {
                            float u = i > wd2 ? i - fimg.Width : i;
                            float v = j > hd2 ? j - fimg.Height : j;
                            float factor = 1.0f - (float)Math.Exp(-(u * u + v * v) / (D0 * D0 * 2));
                            fimg.ValuesB[j * fimg.Width + i] *= factor;
                            fimg.ValuesG[j * fimg.Width + i] *= factor;
                            fimg.ValuesR[j * fimg.Width + i] *= factor;
                        }
                    }
                });
            }
        }

        private void ButterworthHighPass(Document doc)
        {
            int D0 = 100;
            int N = 1;
            ButterworthForm form = new ButterworthForm();
            form.SetOrderEnabled(true);
            form.Text = "Butterworth High Pass Filter";
            if (form.ShowDialog() == DialogResult.OK)
            {
                D0 = form.GetRadius();
                N = form.GetOrder();
                FrequencyTransformation(doc, (FourierImage fimg) =>
                {
                    int wd2 = fimg.Width / 2;
                    int hd2 = fimg.Height / 2;
                    for (int j = 0; j < fimg.Height; j++)
                    {
                        for (int i = 0; i < fimg.Width; i++)
                        {
                            float u = i > wd2 ? i - fimg.Width : i;
                            float v = j > hd2 ? j - fimg.Height : j;
                            float factor = 1.0f / (1.0f + (float)(D0/Math.Pow(Math.Sqrt(u * u + v * v), 2 * N)));
                            fimg.ValuesB[j * fimg.Width + i] *= factor;
                            fimg.ValuesG[j * fimg.Width + i] *= factor;
                            fimg.ValuesR[j * fimg.Width + i] *= factor;
                        }
                    }
                });
            }
        }

        private void ShowSpectrum(Document doc)
        {
            FourierImage fimg = ImageFourierTransform(doc,false);
            if (fimg != null)
            {
                FourierResultForm form = new FourierResultForm();
                form.Doc.SetBitmap(fimg.GetSpectrumBitmap(form.ampleTrackBar.Value));
                form.View.ViewMode = ViewMode.BestFit;
                form.ampleTrackBar.ValueChanged += (Object sender, EventArgs e) =>
                {
                    form.Doc.SetBitmap(fimg.GetSpectrumBitmap(form.ampleTrackBar.Value));
                    form.View.Refresh();
                };
                form.ShowDialog();
            }
        }

        private delegate void TransformDelegate(FourierImage fimg);

        private bool FrequencyTransformation(Document doc, TransformDelegate transform)
        {
            FourierImage fimg = ImageFourierTransform(doc,false);
            if (fimg != null)
            {
                transform(fimg);
                Bitmap bitRs = ImageInverseFourierTransform(fimg, false);
                if (bitRs != null)
                {
                    doc.SetBitmap(bitRs);
                    app.ImageView.Refresh();
                    return true;
                }
            }
            return false;
        }

        private Bitmap ImageInverseFourierTransform(FourierImage fimg, bool exp)
        {
            bool cancel = false;
            Bitmap bitRs = null;
            FFTStatusForm statForm = new FFTStatusForm();
            statForm.SetStatus("Inverse FFT...");
            statForm.Load += (Object s, EventArgs e1) =>
            {
                Thread thd = new Thread(new ThreadStart(() =>
                {
                    try
                    {
                        bitRs = fimg.ToOriginalBitmap(exp, () => (cancel),
                            (int value, int max) =>
                            {
                                app.ImageView.Invoke(new NotifyEvent(() =>
                                {
                                    statForm.SetProgress(value, max);
                                }));
                            });
                        if (!cancel)
                        {
                            app.ImageView.Invoke(new NotifyEvent(() =>
                            {
                                statForm.DialogResult = DialogResult.OK;
                            }
                            ));
                        }
                    }
                    catch
                    {
                        MessageBox.Show("Prime factor of image size is too large.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        statForm.DialogResult = DialogResult.Cancel;
                    }
                }
                ));
                thd.Start();
            };

            statForm.Cancelled += () =>
            {
                cancel = true;

            };
            if (statForm.ShowDialog() == DialogResult.OK)
            {
                return bitRs;
            }
            else
            {
                statForm.Close();
                return null;
            }
        }

        private FourierImage ImageFourierTransform(Document doc, bool log)
        {
            bool cancel = false;
            FourierImage fimg = null;
            
            FFTStatusForm statForm = new FFTStatusForm();
            statForm.Load += (Object s, EventArgs e1) =>
            {
                Thread thd = new Thread(new ThreadStart(() =>
                {
                    try
                    {
                        fimg = FourierTransform.FourierImage.FromImage(doc.GetBitmap(), log, () => (cancel),
                            (int value, int max) =>
                            {
                                app.ImageView.Invoke(new NotifyEvent(() =>
                                {
                                    statForm.SetProgress(value, max);
                                }));
                            });
                        if (!cancel)
                        {
                            app.ImageView.Invoke(new NotifyEvent(() =>
                            {
                                statForm.DialogResult = DialogResult.OK;
                            }
                            ));
                        }
                    }
                    catch
                    {
                        MessageBox.Show("Prime factor of image size is too large.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        statForm.DialogResult = DialogResult.Cancel;
                    }
                }
                ));
                thd.Start();
            };

            statForm.Cancelled += () =>
            {
                cancel = true;

            };
            if (statForm.ShowDialog() == DialogResult.OK)
            {
                return fimg;
            }
            else
            {
                statForm.Close();
                return null;
            }
        }

        public void RegisterMenu(IMenuRegistry registry)
        {
            registry.RegisterMenuItem("Anal&ysis", "Image &Spectrum...", "ShowSpectrum",Keys.None, this);
            registry.RegisterMenuItem("F&ilter", "Butterworth &Low Pass Filter...", "ButterworthLowPass", Keys.None, this);
            registry.RegisterMenuItem("F&ilter", "Butterworth &High Pass Filter...", "ButterworthHighPass", Keys.None, this);
            registry.RegisterMenuItem("F&ilter", "&Guassian Low Pass Filter...", "GuassianLowPass", Keys.None, this);
            registry.RegisterMenuItem("F&ilter", "G&uassian High Pass Filter...", "GuassianHighPass", Keys.None, this);
            
        }

        #endregion
    }
}
